/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2019年12月20日
 * V4.0
 */
package com.jphenix.driver.serialize;

import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.jphenix.driver.json.Json;
import com.jphenix.driver.nodehandler.FNodeHandler;
import com.jphenix.kernel.baseobject.instanceb.ABase;
import com.jphenix.servlet.common.ActionContext;
import com.jphenix.servlet.multipart.instancea.UploadFile;
import com.jphenix.share.lang.SDate;
import com.jphenix.share.lang.SListMap;
import com.jphenix.share.tools.Base64;
import com.jphenix.share.tools.FileCopyTools;
import com.jphenix.share.util.BaseUtil;
import com.jphenix.share.util.StringUtil;
import com.jphenix.standard.docs.BeanInfo;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.exceptions.MsgException;
import com.jphenix.standard.servlet.IActionContext;
import com.jphenix.standard.servlet.IRequest;
import com.jphenix.standard.servlet.IResponse;
import com.jphenix.standard.viewhandler.INodeHandler;
import com.jphenix.standard.viewhandler.IViewHandler;

/**
 * 将对象实例序列化成XML格式字符串
 * com.jphenix.driver.serialize.XmlSerializer
 * 
 * 2020-01-20 修改了处理ViewHandler对象错误
 *            增加了解析返回的异常对象，通过参数判断是否抛出该异常
 * 2020-03-18 修改了远程方法调用传递参数解析时报错
 * 2020-03-28 增加了MsgException异常序列与反序列化
 * 2020-04-27 增加了远程调用无返回值处理
 * 2020-07-02 修改了序列化时拼装XML的bug
 * 2020-07-11 去掉了替代null的对象，容易报错
 * 2021-03-16 整理了代码
 * 
 * 
 * @author MBG
 * 2019年12月20日
 */
@BeanInfo({"xmlserializer"})
@ClassInfo({"2021-03-16 15:34","将对象实例序列化成XML格式字符串"})
public class XmlSerializer extends ABase implements ISerializer {

  /**
   * 构造函数
   * @author MBG
   */
  public XmlSerializer() {
    super();
  }

  /**
   * 将类实例序列化成XML格式字符串
   * 注意：该方法返回的并不是完整的XML，只是参数被序列化成XML信息段
   * @param obj 类实例
   * @return    对应的XML格式字符串
   * 2019年12月20日
   * @author MBG
   */
  @SuppressWarnings({ "rawtypes", "unchecked" })
  public String serialize(Object obj) {
    if(obj==null) {
      return "<v t=\"NULL\"></v>";
    }
    StringBuffer res = new StringBuffer(); //构建返回值
    String typeName;                       //类型名
    if(obj.getClass().isArray()) {
      //该值是否为数组
      typeName = obj.getClass().getName();
      typeName = typeName.substring(2,typeName.length()-1); //去掉[Ljava.lang.String;开头的[L和末尾的;
      res.append("<v array=\"1\" t=\"").append(typeName).append("\">");
      int count = Array.getLength(obj); //数组数量
      for(int i=0;i<count;i++) {
        res.append(serialize(Array.get(obj,i)));
      }
      res.append("</v>");
      return res.toString();
    }
    
    /*
     * 单实例
     */
    if(obj instanceof IActionContext) {
      /*
       * 动作类
       */
      res.append("<v action=\"1\">");
      //获取提交参数容器
      Map<String,String[]> paraMap = ((IActionContext)obj).getRequest().getParameterMap();
      if(paraMap!=null) {
        //参数主键序列
        List<String> keyList = BaseUtil.getMapKeyList(paraMap);
        String[] values; //参数值
        for(String key:keyList) {
          res.append("<k n=\"").append(key).append("\">");
          values = paraMap.get(key);
          if(values!=null && values.length>0) {
            for(int i=0;i<values.length;i++) {
              res.append("<e>").append(values[i]).append("</e>");
            }
          }
          res.append("</k>");
        }
      }
      res.append("</v>");
      return res.toString();
    }
    typeName = obj.getClass().getName();
    if(obj instanceof Map) {
      /*
       * -----Map-----
       * Map容器
       */
      res.append("<v map=\"1\" t=\"").append(typeName).append("\">");
      //获取容器主键序列
      List<String> keys = BaseUtil.getMapKeyList((Map)obj);
      for(String key:keys) {
        res
          .append("<k n=\"").append(key).append("\">")
          .append(serialize(((Map)obj).get(key)))
          .append("</k>");
      }
      res.append("</v>");
    }else if("com.jphenix.share.lang.SListMap".equals(typeName)) {
      /*
       * -----SListMap-----
       * 序列对照容器
       */
      res.append("<v t=\"").append(typeName).append("\">");
      //获取容器主键序列
      List<String> keys = ((SListMap)obj).keys();
      for(String key:keys) {
        res
          .append("<k n=\"").append(key).append("\">")
          .append(serialize(((SListMap)obj).get(key)))
          .append("</k>");
      }
      res.append("</v>");
    }else if(obj instanceof List) {
      /*
       * -----List-----
       * List对象序列
       */
      res.append("<v list=\"1\" t=\"").append(typeName).append("\">");
      for(Object ele:((List)obj)) {
        res.append(serialize(ele));
      }
      res.append("</v>");
    }else if("com.jphenix.standard.viewhandler.IViewHandler".equals(typeName)) {
      /*
       * -----IViewHandler-----
       * XML对象
       */
      res
        .append("<v t=\"").append(typeName).append("\" action=\"1\">")
        .append(Base64.base64Encode(((IViewHandler)obj).getNodeBody(),"UTF-8"))
        .append("</v>");
    }else if(obj instanceof MsgException) {
      /*
       * -----MsgException-----
       * 消息异常
       */
      res
        .append("<v msg_exception=\"1\" head=\">").append(((MsgException)obj).getHead())
        .append("\" code=\"").append(((MsgException)obj).getCode()).append("\">")
        .append(Base64.base64Encode(((MsgException) obj).getMessage(),"UTF-8")).append("</v>");
    }else if(obj instanceof Exception) {
      /*
       * -----Exception-----
       * 异常
       */
      res
        .append("<v exception=\"1\" t=\"").append(typeName).append("\">")
        .append(Base64.base64Encode(((Exception) obj).getMessage(),"UTF-8")).append("</v>");
    }else if("java.io.File".equals(typeName)) {
      /*
       * -----File-----
       * 文件对象
       */
      try {
        res
          .append("<v t=\"").append(typeName).append("\" n=\"")
          .append(((File)obj).getName()).append("\" l=\"")
          .append(((File)obj).length()).append("\"><![CDATA[")
          .append(Base64.base64Encode(FileCopyTools.copyToByteArray((File)obj)))
          .append("]]></v>"); 
      }catch(Exception e) {
        e.printStackTrace();
      }
    }else if("com.jphenix.servlet.multipart.instancea.UploadFile".equals(typeName)) {
      /*
       * -----UploadFile-----
       * 文件上传对象
       */
      try {
        File pFile = ((UploadFile)obj).getFile();
        res.append("<v t=\"").append(typeName).append("\" n=\"")
          .append(((UploadFile)obj).srcFileName).append("\" l=\"")
          .append(pFile.length()).append("\"><![CDATA[")
          .append(Base64.base64Encode(FileCopyTools.copyToByteArray(pFile)))
          .append("]]></v>"); 
      }catch(Exception e) {
        e.printStackTrace();
      }
    }else if(obj instanceof ISerializable) {
      /*
       * -----ISerializable-----
       */
      res.append("<v iserializable=\"1\" t=\"").append(typeName).append("\">").append(Base64.base64Encode(((ISerializable)obj).serialize(),"UTF-8")).append("</v>");
    }else {
      /*
       * String int Integer long Long doble Double float Float Json boolean Boolean
       */
      res.append("<v t=\"").append(typeName).append("\">").append(Base64.base64Encode(obj.toString(),"UTF-8")).append("</v>");
    }
    return res.toString();
  }

  /**
   * 将XML格式字符串反序列化为对应的类实例
   * @param xmlStr           XML格式字符串
   * @param req              请求对象（可为空，只有处理IActionContext实例时才会用到）
   * @param resp             反馈对象（可为空，只有处理IActionContext实例时才会用到）
   * @param triggerException 如果解析的值未异常对象，是否触发抛出该异常
   * @return                 对应的类实例
   * @throws Exception       异常
   */
  public Object unserialize(String xmlStr,IRequest req,IResponse resp,boolean triggerException) throws Exception {
    //构建XML对象
    INodeHandler infoXml = FNodeHandler.newNodeHandler();
    infoXml.setNodeBody(xmlStr);
    return unserialize(infoXml,req,resp,triggerException);
  }

  /**
   * 将XML格式字符串反序列化为对应的类实例
   * @param xmlStr           XML格式字符串
   * @param triggerException 如果解析的值未异常对象，是否触发抛出该异常
   * @return                 对应的类实例
   * @throws Exception       异常
   * 2019年12月23日
   * @author MBG
   */
  public Object unserialize(String xmlStr,boolean triggerException) throws Exception {
    //构建XML对象
    INodeHandler infoXml = FNodeHandler.newNodeHandler();
    infoXml.setNodeBody(xmlStr);
    return unserialize(infoXml,null,null,triggerException);
  }

  /**
   * 将XML对象反序列化为对应的类实例
   * @param node             XML对象
   * @param req              请求对象（可为空，只有处理IActionContext实例时才会用到）
   * @param resp             反馈对象（可为空，只有处理IActionContext实例时才会用到）
   * @param triggerException 如果解析的值未异常对象，是否触发抛出该异常
   * @return                 对应的类实例
   * 2019年12月20日
   * @author MBG
   */
  @SuppressWarnings({ "rawtypes", "unchecked" })
  public Object unserialize(
    IViewHandler node,
    IRequest     req,
    IResponse    resp,
    boolean      triggerException) throws Exception {
    if(node==null) {
      return null;
    }
    if(!node.nn().equals("v")) {
      node = node.getFirstChildNodeByNodeName("v");
    }
    String type = node.a("t"); //对象类型
    if("NULL".equals(type)) {
      //返回空
      return null;
    }
    Object res       = null; //返回值
    String typeName  = null; //类型名
    if("1".equals(node.a("array"))) {
      //数组对象
      List<IViewHandler> clist = node.ocnn("v"); //返回一级子节点序列
      typeName                 = node.a("t");    //类型名
      //构造数组
      res = Array.newInstance(Class.forName(typeName),clist.size());
      for(int i=0;i<clist.size();i++) {
        Array.set(res,i,unserialize(clist.get(i),req,resp,triggerException));
      }
      return res;
    }
    if("1".equals(node.a("action"))) {
      /*
       * ----------action-----------
       */
      //动作上下文
        ActionContext ac = new ActionContext(req,resp,req.getServerName().toLowerCase(),"","");
      //提交参数容器
        Map<String,String[]> paraMap = req.getParameterMap();
        //获取提交参数容器
        List<IViewHandler> eleNodes = node.ocnn("k");
        String key; //参数主键
        List<String> valueList;
        List<IViewHandler> valueNodes;
        for(IViewHandler ele:eleNodes) {
          key = ele.a("name");
          valueList = new ArrayList<String>();
          valueNodes = ele.ocnn("e");
          for(IViewHandler cele:valueNodes) {
            valueList.add(cele.nt());
          }
          paraMap.put(key,StringUtil.list2strs(valueList));
        }
        return ac;
    }
    //消息异常 该对象不需要反射构建
    if("1".equals(node.a("msg_exception"))) {
      //构建异常
      MsgException me = new MsgException(node.a("head"),Base64.base64Decode(node.nt(),"UTF-8"),node.a("code"));
      if(triggerException) {
        throw me;
      }
      return me;
    }
    /*
     * 单元素对象
     */
    typeName    = node.a("t");
    if(typeName.length()<1) {
      return null;
    }
    //返回值类型
    Class reCls = null;
    try {
      reCls = Class.forName(typeName);
    }catch(Exception e) {
      if(log==null) {
        System.err.println("Class.forName:("+typeName+") Exception happend; XML:[\n"+node.getNodeBody()+"\n]");
      }else {
        error("Class.forName:("+typeName+") Exception happend; XML:[\n"+node.getNodeBody()+"\n]");
      }
      throw e;
    }
    //异常
    if("1".equals(node.a("exception"))) {
      //构建异常
      Constructor constructor = reCls.getConstructor(new Class[] {String.class});
      res = constructor.newInstance(new Object[] {Base64.base64Decode(node.nt(),"UTF-8")});
      if(triggerException) {
        throw (Exception)res;
      }
      return res;
    }
    if("1".equals(node.a("iserializable"))) {
      /*
       * ----------ISerializable-----------
       */
      //自行序列化的类
      res = reCls.newInstance();
      ((ISerializable)res).unserialize(Base64.base64Decode(node.nt(),"UTF-8"));
    }else if("1".equals(node.a("map"))) {
      /*
       * ----------Map-----------
       */
      res                     = reCls.newInstance(); //对照容器
      List<IViewHandler> keys = node.ocnn("k");      //元素信息序列
      for(IViewHandler ele:keys) {
        ((Map)res).put(ele.a("n"),unserialize(ele.fc(),req,resp,triggerException));
      }
    }else if("com.jphenix.share.lang.SListMap".equals(typeName)) {
      /*
       * ----------SListMap-----------
       */
      res                     = new SListMap();      //对照容器
      List<IViewHandler> keys = node.ocnn("k");      //元素信息序列
      for(IViewHandler ele:keys) {
        ((SListMap)res).put(ele.a("n"),unserialize(ele.fc(),req,resp,triggerException));
      }
    }else if("1".equals(node.a("list"))) {
      /*
       * ----------List-----------
       */
      res                     = reCls.newInstance(); //序列对象
      List<IViewHandler> keys = node.ocnn("v");      //元素信息序列
      for(IViewHandler ele:keys) {
        ((List)res).add(unserialize(ele,req,resp,triggerException));
      }
    }else if("com.jphenix.standard.viewhandler.IViewHandler".equals(typeName) 
        || "com.jphenix.driver.nodehandler.instancea.NodeHandler".equals(typeName)) {
      /*
       * ----------IViewHandler-----------
       */
      res = FNodeHandler.newBody(Base64.base64Decode(node.nt(),"UTF-8"));
    }else if("java.io.File".equals(typeName)) {
      /*
       * ----------File-----------
       */
      res = tmpFile("/"+SDate.nowThinDate()+"/"+node.a("name"));
      //解析文件数据
      FileCopyTools.copy(Base64.decode64(node.nt()),(File)res);
    }else if("com.jphenix.servlet.multipart.instancea.UploadFile".equals(typeName)) {
      /*
       * ----------UploadFile-----------
       */
      File file = tmpFile("/"+SDate.nowThinDate()+"/"+node.a("name"));
      //解析文件数据
      FileCopyTools.copy(Base64.decode64(node.nt()),file);
      res = UploadFile.file(file,node.a("name"));
    }else if("com.jphenix.driver.json.Json".equals(typeName)) {
      /*
       * ----------Json-----------
       */
      res = new Json(Base64.base64Decode(node.nt(),"UTF-8"));
    }else if("java.lang.String".equals(typeName)) {
      /*
       * ----------String-----------
       */
      res = Base64.base64Decode(node.nt(),"UTF-8");
    }else if("int".equals(typeName) || "java.lang.Integer".equals(typeName)) {
      /*
       * ----------int Integer-----------
       */
      res = sint(Base64.base64Decode(node.nt(),"UTF-8"));
    }else if("long".equals(typeName) || "java.lang.Long".equals(typeName)) {
      /*
       * ----------long Long-----------
       */
      res = lon(Base64.base64Decode(node.nt(),"UTF-8"));
    }else if("float".equals(typeName) 
        || "double".equals(typeName) 
        || "java.lang.Float".equals(typeName)
        || "java.lang.Double".equals(typeName)) {
      /*
       * ----------float Float double Double-----------
       */
      res = dou(Base64.base64Decode(node.nt(),"UTF-8"));
    }else if("long".equals(typeName) || "java.lang.Long".equals(typeName)) {
      /*
       * ----------boolean Boolean-----------
       */
      res = boo(Base64.base64Decode(node.nt(),"UTF-8"));
    }else if("byte".equals(typeName) || "java.lang.Byte".equals(typeName)) {
      /*
       * ----------byte Byte-----------
       */
      byte[] bytes = str(Base64.base64Decode(node.nt(),"UTF-8")).getBytes();
      if(bytes!=null && bytes.length>0) {
        res = bytes[0];
      }else {
        res = null;
      }
    }else {
      res = Base64.base64Decode(node.nt(),"UTF-8");
    }
    return res;
  }
}
